#include "iConfig.hpp"

#if GLF_VIRTUAL_FILESTREAM

#include <glf/io/fileStream.h>

namespace glf {

FileStream::FileStream()
	: mSize(0)
	, mStatus(STATUS_CLOSED)
	, mCounter(0)
	, mFlag(ios::none)
	, mOffset(0)
{
}

FileStream::FileStream(const char* filename, ios::openflag flag)
	: mSize(0)
	, mStatus(STATUS_CLOSED)
	, mCounter(0)
	, mFlag(ios::none)
	, mOffset(0)
{
	Open(filename, flag);
}

FileStream::~FileStream()
{
	Close();
}

bool FileStream::Open(const char* filename, ios::openflag flag)
{
	bool success = GetGlobals()->fileStreamMgr.Open(this, filename, flag);
	
	if(success)
	{
		mSize = mFileStream.GetSize();
	}

	return success;
}

void FileStream::Close()
{
	WaitASync(-1);
	if(IsOpened())
	{
		GetGlobals()->fileStreamMgr.Close(this);
	}
	mSize = 0;
}

size_t FileStream::GetSize() const
{
	return mSize;
}

bool FileStream::Seek(int offset, ios::seek origin)
{
	GetGlobals()->fileStreamMgr.Ensure(this);
	return mFileStream.Seek(offset, origin);
}

size_t FileStream::Read(void* buffer, size_t size)
{
	GetGlobals()->fileStreamMgr.Ensure(this);
	return mFileStream.Read(buffer, size);
}

void FileStream::Check()
{
	mCounter = GetGlobals()->fileStreamMgr.IncrCounter();
}

uint64 FileStream::GetCounter() const
{
	return mCounter;
}

size_t FileStream::Peek(void* buffer, size_t size)
{
	GetGlobals()->fileStreamMgr.Ensure(this);
	return mFileStream.Peek(buffer, size);
}

bool FileStream::CancelAsync(FileStreamImpl::Callback callback, void* arg)
{
	return mFileStream.CancelAsync(callback, arg);
}
	
bool FileStream::CanSeek() const
{
	return mFileStream.CanSeek();
}

bool FileStream::CanRead() const
{
	return mFileStream.CanRead();
}

bool FileStream::CanWrite() const
{
	return mFileStream.CanWrite();
}

int FileStream::WaitASync(size_t ms) const
{
	return mFileStream.WaitASync(ms);
}

size_t FileStream::Tell() const
{
	// TODO: The IOStream class keep a copy of the position in the file. This could
	//       allow some optimization as it won't rely on the system tell, which 
	//       require the filesystem to re-open the file. All it requires is some
	//       unit test to make sure the IOStream position == system position.
	//
	//       The IsEof method would also benefit from this optimization since it
	//       relies on the Tell method internally.
	FileStream* t = const_cast<FileStream*>(this);
	GetGlobals()->fileStreamMgr.Ensure(t);
	return mFileStream.Tell();
}

bool FileStream::IsEof() const
{
	FileStream* t = const_cast<FileStream*>(this);
	GetGlobals()->fileStreamMgr.Ensure(t);
	return mFileStream.IsEof();
}

bool FileStream::IsOpened() const
{
	return mStatus == STATUS_OPENED || mStatus == STATUS_OUT;
}

bool FileStream::IsBusy() const
{
	return mFileStream.IsBusy();
}

size_t FileStream::ReadWithOffset(void* buffer, size_t size, size_t offset)
{
	GetGlobals()->fileStreamMgr.Ensure(this);
	return mFileStream.ReadWithOffset(buffer, size, offset);
}

bool FileStream::ReadASync(void* buffer, size_t size, size_t offset, FileStreamImpl::Callback callback, void* arg)
{
	return mFileStream.ReadASync(this, buffer, size, offset, callback, arg);
}

size_t FileStream::Write(const void* buffer, size_t size)
{
	GetGlobals()->fileStreamMgr.Ensure(this);
	return mFileStream.Write(buffer, size);
}

ios::openflag FileStream::CopyPath(char* dst, size_t len) const
{
	return mFileStream.CopyPath(dst, len);
}

const char* FileStream::GetPath(ios::openflag& loc) const
{
	return mFileStream.GetPath(loc);
}

const char* FileStream::GetFilename() const
{
	return mFileStream.GetFilename();
}

void FileStream::DispatchDoneReads()
{
	return FileStreamImpl::DispatchDoneReads();
}

bool FileStream::OpenImpl(const char* filename, ios::openflag flag)
{
	mFilename = filename;
	mFlag = flag;

	bool ret = mFileStream.Open(filename, flag);
	if(ret)
	{
		mStatus = STATUS_OPENED;
	}
	
	return ret;
}

void FileStream::CloseImpl()
{
	mStatus = STATUS_CLOSED;
	mFilename.clear();
	mOffset = 0;

	mFileStream.Close();
}

void FileStream::FileOut()
{
	if(mStatus != STATUS_OPENED)
	{
		return;
	}

	mStatus = STATUS_OUT;
	mOffset = mFileStream.Tell();

	mFileStream.Close();
}

void FileStream::FileIn()
{
	if(mStatus != STATUS_OUT)
	{
		return;
	}

	mStatus = STATUS_OPENED;

	bool ret = mFileStream.Open(mFilename.c_str(), mFlag);
	GLF_ASSERT(ret);	// This means the file "vanished" between origin opening and the file in re-openinig...

	mFileStream.Seek(mOffset, ios::beg);
}

} // end namespace glf

#endif // GLF_VIRTUAL_FILESTREAM
